/**
 * ImjvFilterInvocationDefinitionSource.java
 */
package com.rizzo.web.security;

import org.springframework.security.ConfigAttributeDefinition;
import org.springframework.security.ConfigAttributeEditor;
import org.springframework.security.intercept.web.FilterInvocation;
import org.springframework.util.AntPathMatcher;
import org.springframework.util.PathMatcher;

import java.util.ArrayList;
import java.util.Collection;
import java.util.List;

/**
 * Own implementation for the objectDefinitionSource that will lookup the
 * attributes for the {@link org.springframework.security.intercept.web.FilterSecurityInterceptor} that
 * will lock the functionality down.
 *
 * @author jalieven
 */
public class RizzoFilterInvocationDefinitionSource implements org.springframework.security.intercept.web.FilterInvocationDefinitionSource {

    /** Field rolesService  */
    private RolesService rolesService;

    /** Field attributeMap  */
    private List<RoleUrl> attributeMap;

    /** Field pathMatcher  */
    private PathMatcher pathMatcher = new AntPathMatcher();

    /**
     * {@inheritDoc}
     */
    public ConfigAttributeDefinition getAttributes(Object object)
            throws IllegalArgumentException {
        if ((object == null) || !this.supports(object.getClass())) {
            throw new IllegalArgumentException(
                    "Object must be a FilterInvocation");
        }

        String url = ((FilterInvocation) object).getRequestUrl();

        return this.lookupAttributes(url);
    }

    /**
     * Looks up the invoked url for the necessary roles that must be attached to
     * the url i.e. a lookup in the attributeMap (initialized in the
     * getConfigAttributeDefinitions() method).
     *
     * @param url
     * @return
     */
    public ConfigAttributeDefinition lookupAttributes(String url) {
        if (attributeMap == null || attributeMap.isEmpty()) {
            // get all the roles for the given url from the facade (as a Map of
            // String-key-value pairs)
            this.attributeMap = this.rolesService.getUrlRolesMap();
        }

        ConfigAttributeDefinition configAttributeDefinition = null;
        // look-up the url as key in the attributeMap if nothing found then
        // return null
        boolean found = false;
        for (RoleUrl roleUrl : attributeMap) {
            boolean matched = pathMatcher.match(roleUrl.getUrl(), url);
            if (matched && !found) {
                ConfigAttributeEditor configAttributeEditor = new ConfigAttributeEditor();
                configAttributeEditor.setAsText(roleUrl.getRoles());
                configAttributeDefinition = (ConfigAttributeDefinition) configAttributeEditor
                        .getValue();
                found = true;
            }
        }
        return configAttributeDefinition;
    }

    /**
     * {@inheritDoc}
     */
    @SuppressWarnings("unchecked")
    public boolean supports(Class clazz) {
        return FilterInvocation.class.isAssignableFrom(clazz);
    }

    /**
     * Looks up the attributes from the rolesFacade that is configured using an
     * own implementation of xml-config-file. This method maps the application
     * specific roles to the different urls. For a given url (with * and ?
     * included) what roles does the user must have in order to perform the
     * function? This method is first called when initiated so here we fill the
     * attributeMap that will be looked up with the lookupAttributes(String url)
     * method with each invocation of an url. It can simply return null.
     *
     * @return
     */
    public java.util.Collection getConfigAttributeDefinitions() {
        Collection<ConfigAttributeDefinition> configAttributeDefinitions = new ArrayList<ConfigAttributeDefinition>();
        if (attributeMap == null || attributeMap.isEmpty()) {
            // get all the roles for the given url from the facade (as a Map of String-key-value pairs)
            this.attributeMap = this.rolesService.getUrlRolesMap();
        }
        for (RoleUrl roleUrl : attributeMap) {
            ConfigAttributeEditor configAttributeEditor = new ConfigAttributeEditor();
            configAttributeEditor.setAsText(roleUrl.getRoles());
            ConfigAttributeDefinition configAttributeDefinition = (ConfigAttributeDefinition) configAttributeEditor.getValue();
            configAttributeDefinitions.add(configAttributeDefinition);
        }
        return configAttributeDefinitions;
    }

    /**
     * Setter for property 'rolesService'.
     *
     * @param rolesService Value to set for property 'RolesService'.
     */
    public void setRolesService(RolesService rolesService) {
        this.rolesService = rolesService;
    }
}